#include "PearAlgorithm.h"


HObject  LEPImage;
HObject ExpGetGlobalVar_LEPImage(void)
{
    return LEPImage;
}
void ExpSetGlobalVar_LEPImage(HObject obj)
{
    if (!obj.IsInitialized())
        return;
    LEPImage = obj;
}

// Procedures
// Chapter: File
// Short Description: Parse a filename into directory, base filename, and extension
void parse_filename (HTuple hv_FileName, HTuple *hv_BaseName, HTuple *hv_Extension,
                    HTuple *hv_Directory)
{

    // Local control variables
    HTuple  hv_DirectoryTmp, hv_Substring;

    //This procedure gets a filename (with full path) as input
    //and returns the directory path, the base filename and the extension
    //in three different strings.
    //
    //In the output path the path separators will be replaced
    //by '/' in all cases.
    //
    //The procedure shows the possibilities of regular expressions in HALCON.
    //
    //Input parameters:
    //FileName: The input filename
    //
    //Output parameters:
    //BaseName: The filename without directory description and file extension
    //Extension: The file extension
    //Directory: The directory path
    //
    //Example:
    //basename('C:/images/part_01.png',...) returns
    //BaseName = 'part_01'
    //Extension = 'png'
    //Directory = 'C:\\images\\' (on Windows systems)
    //
    //Explanation of the regular expressions:
    //
    //'([^\\\\/]*?)(?:\\.[^.]*)?$':
    //To start at the end, the '$' matches the end of the string,
    //so it is best to read the expression from right to left.
    //The part in brackets (?:\\.[^.}*) denotes a non-capturing group.
    //That means, that this part is matched, but not captured
    //in contrast to the first bracketed group ([^\\\\/], see below.)
    //\\.[^.]* matches a dot '.' followed by as many non-dots as possible.
    //So (?:\\.[^.]*)? matches the file extension, if any.
    //The '?' at the end assures, that even if no extension exists,
    //a correct match is returned.
    //The first part in brackets ([^\\\\/]*?) is a capture group,
    //which means, that if a match is found, only the part in
    //brackets is returned as a result.
    //Because both HDevelop strings and regular expressions need a '\\'
    //to describe a backslash, inside regular expressions within HDevelop
    //a backslash has to be written as '\\\\'.
    //[^\\\\/] matches any character but a slash or backslash ('\\' in HDevelop)
    //[^\\\\/]*? matches a string od 0..n characters (except '/' or '\\')
    //where the '?' after the '*' switches the greediness off,
    //that means, that the shortest possible match is returned.
    //This option is necessary to cut off the extension
    //but only if (?:\\.[^.]*)? is able to match one.
    //To summarize, the regular expression matches that part of
    //the input string, that follows after the last '/' or '\\' and
    //cuts off the extension (if any) after the last '.'.
    //
    //'\\.([^.]*)$':
    //This matches everything after the last '.' of the input string.
    //Because ([^.]) is a capturing group,
    //only the part after the dot is returned.
    //
    //'.*[\\\\/]':
    //This matches the longest substring with a '/' or a '\\' at the end.
    //
    TupleRegexpMatch(hv_FileName, ".*[\\\\/]", &hv_DirectoryTmp);
    TupleSubstr(hv_FileName, hv_DirectoryTmp.TupleStrlen(), (hv_FileName.TupleStrlen())-1,
                &hv_Substring);
    TupleRegexpMatch(hv_Substring, "([^\\\\/]*?)(?:\\.[^.]*)?$", &(*hv_BaseName));
    TupleRegexpMatch(hv_Substring, "\\.([^.]*)$", &(*hv_Extension));
    //
    //
    //Finally all found backslashes ('\\') are converted
    //to a slash to get consistent paths
    TupleRegexpReplace(hv_DirectoryTmp, (HTuple("\\\\").Append("replace_all")), "/",
                       &(*hv_Directory));
    return;
}

// Chapter: Graphics / Text
// Short Description: This procedure writes a text message.
void disp_message (HTuple hv_WindowHandle, HTuple hv_String, HTuple hv_CoordSystem,
                  HTuple hv_Row, HTuple hv_Column, HTuple hv_Color, HTuple hv_Box)
{

    // Local iconic variables

    // Local control variables
    HTuple  hv_Red, hv_Green, hv_Blue, hv_Row1Part;
    HTuple  hv_Column1Part, hv_Row2Part, hv_Column2Part, hv_RowWin;
    HTuple  hv_ColumnWin, hv_WidthWin, hv_HeightWin, hv_MaxAscent;
    HTuple  hv_MaxDescent, hv_MaxWidth, hv_MaxHeight, hv_R1;
    HTuple  hv_C1, hv_FactorRow, hv_FactorColumn, hv_UseShadow;
    HTuple  hv_ShadowColor, hv_Exception, hv_Width, hv_Index;
    HTuple  hv_Ascent, hv_Descent, hv_W, hv_H, hv_FrameHeight;
    HTuple  hv_FrameWidth, hv_R2, hv_C2, hv_DrawMode, hv_CurrentColor;

    //This procedure displays text in a graphics window.
    //
    //Input parameters:
    //WindowHandle: The WindowHandle of the graphics window, where
    //   the message should be displayed
    //String: A tuple of strings containing the text message to be displayed
    //CoordSystem: If set to 'window', the text position is given
    //   with respect to the window coordinate system.
    //   If set to 'image', image coordinates are used.
    //   (This may be useful in zoomed images.)
    //Row: The row coordinate of the desired text position
    //   If set to -1, a default value of 12 is used.
    //Column: The column coordinate of the desired text position
    //   If set to -1, a default value of 12 is used.
    //Color: defines the color of the text as string.
    //   If set to [], '' or 'auto' the currently set color is used.
    //   If a tuple of strings is passed, the colors are used cyclically
    //   for each new textline.
    //Box: If Box[0] is set to 'true', the text is written within an orange box.
    //     If set to' false', no box is displayed.
    //     If set to a color string (e.g. 'white', '#FF00CC', etc.),
    //       the text is written in a box of that color.
    //     An optional second value for Box (Box[1]) controls if a shadow is displayed:
    //       'true' -> display a shadow in a default color
    //       'false' -> display no shadow (same as if no second value is given)
    //       otherwise -> use given string as color string for the shadow color
    //
    //Prepare window
    GetRgb(hv_WindowHandle, &hv_Red, &hv_Green, &hv_Blue);
    GetPart(hv_WindowHandle, &hv_Row1Part, &hv_Column1Part, &hv_Row2Part, &hv_Column2Part);
    GetWindowExtents(hv_WindowHandle, &hv_RowWin, &hv_ColumnWin, &hv_WidthWin, &hv_HeightWin);
    SetPart(hv_WindowHandle, 0, 0, hv_HeightWin-1, hv_WidthWin-1);
    //
    //default settings
    if (0 != (hv_Row==-1))
    {
        hv_Row = 12;
    }
    if (0 != (hv_Column==-1))
    {
        hv_Column = 12;
    }
    if (0 != (hv_Color==HTuple()))
    {
        hv_Color = "";
    }
    //
    hv_String = ((""+hv_String)+"").TupleSplit("\n");
    //
    //Estimate extentions of text depending on font size.
    GetFontExtents(hv_WindowHandle, &hv_MaxAscent, &hv_MaxDescent, &hv_MaxWidth, &hv_MaxHeight);
    if (0 != (hv_CoordSystem==HTuple("window")))
    {
        hv_R1 = hv_Row;
        hv_C1 = hv_Column;
    }
    else
    {
        //Transform image to window coordinates
        hv_FactorRow = (1.*hv_HeightWin)/((hv_Row2Part-hv_Row1Part)+1);
        hv_FactorColumn = (1.*hv_WidthWin)/((hv_Column2Part-hv_Column1Part)+1);
        hv_R1 = ((hv_Row-hv_Row1Part)+0.5)*hv_FactorRow;
        hv_C1 = ((hv_Column-hv_Column1Part)+0.5)*hv_FactorColumn;
    }
    //
    //Display text box depending on text size
    hv_UseShadow = 1;
    hv_ShadowColor = "gray";
    if (0 != (HTuple(hv_Box[0])==HTuple("true")))
    {
        hv_Box[0] = "#fce9d4";
        hv_ShadowColor = "#f28d26";
    }
    if (0 != ((hv_Box.TupleLength())>1))
    {
        if (0 != (HTuple(hv_Box[1])==HTuple("true")))
        {
            //Use default ShadowColor set above
        }
        else if (0 != (HTuple(hv_Box[1])==HTuple("false")))
        {
            hv_UseShadow = 0;
        }
        else
        {
            hv_ShadowColor = ((const HTuple&)hv_Box)[1];
            //Valid color?
            try
            {
                SetColor(hv_WindowHandle, HTuple(hv_Box[1]));
            }
            // catch (Exception)
            catch (HalconCpp::HException &HDevExpDefaultException)
            {
                HDevExpDefaultException.ToHTuple(&hv_Exception);
                hv_Exception = "Wrong value of control parameter Box[1] (must be a 'true', 'false', or a valid color string)";
                throw HalconCpp::HException(hv_Exception);
            }
        }
    }
    if (0 != (HTuple(hv_Box[0])!=HTuple("false")))
    {
        //Valid color?
        try
        {
            SetColor(hv_WindowHandle, HTuple(hv_Box[0]));
        }
        // catch (Exception)
        catch (HalconCpp::HException &HDevExpDefaultException)
        {
            HDevExpDefaultException.ToHTuple(&hv_Exception);
            hv_Exception = "Wrong value of control parameter Box[0] (must be a 'true', 'false', or a valid color string)";
            throw HalconCpp::HException(hv_Exception);
        }
        //Calculate box extents
        hv_String = (" "+hv_String)+" ";
        hv_Width = HTuple();
        {
            HTuple end_val93 = (hv_String.TupleLength())-1;
            HTuple step_val93 = 1;
            for (hv_Index=0; hv_Index.Continue(end_val93, step_val93); hv_Index += step_val93)
            {
                GetStringExtents(hv_WindowHandle, HTuple(hv_String[hv_Index]), &hv_Ascent,
                                 &hv_Descent, &hv_W, &hv_H);
                hv_Width = hv_Width.TupleConcat(hv_W);
            }
        }
        hv_FrameHeight = hv_MaxHeight*(hv_String.TupleLength());
        hv_FrameWidth = (HTuple(0).TupleConcat(hv_Width)).TupleMax();
        hv_R2 = hv_R1+hv_FrameHeight;
        hv_C2 = hv_C1+hv_FrameWidth;
        //Display rectangles
        GetDraw(hv_WindowHandle, &hv_DrawMode);
        SetDraw(hv_WindowHandle, "fill");
        //Set shadow color
        SetColor(hv_WindowHandle, hv_ShadowColor);
        if (0 != hv_UseShadow)
        {
            DispRectangle1(hv_WindowHandle, hv_R1+1, hv_C1+1, hv_R2+1, hv_C2+1);
        }
        //Set box color
        SetColor(hv_WindowHandle, HTuple(hv_Box[0]));
        DispRectangle1(hv_WindowHandle, hv_R1, hv_C1, hv_R2, hv_C2);
        SetDraw(hv_WindowHandle, hv_DrawMode);
    }
    //Write text.
    {
        HTuple end_val115 = (hv_String.TupleLength())-1;
        HTuple step_val115 = 1;
        for (hv_Index=0; hv_Index.Continue(end_val115, step_val115); hv_Index += step_val115)
        {
            hv_CurrentColor = ((const HTuple&)hv_Color)[hv_Index%(hv_Color.TupleLength())];
            if (0 != (HTuple(hv_CurrentColor!=HTuple("")).TupleAnd(hv_CurrentColor!=HTuple("auto"))))
            {
                SetColor(hv_WindowHandle, hv_CurrentColor);
            }
            else
            {
                SetRgb(hv_WindowHandle, hv_Red, hv_Green, hv_Blue);
            }
            hv_Row = hv_R1+(hv_MaxHeight*hv_Index);
            SetTposition(hv_WindowHandle, hv_Row, hv_C1);
            WriteString(hv_WindowHandle, HTuple(hv_String[hv_Index]));
        }
    }
    //Reset changed window settings
    SetRgb(hv_WindowHandle, hv_Red, hv_Green, hv_Blue);
    SetPart(hv_WindowHandle, hv_Row1Part, hv_Column1Part, hv_Row2Part, hv_Column2Part);
    return;
}

// Local procedures
void preprocess (HObject ho_Image, HObject *ho_ResImage, HTuple hv_Alpha)
{

    // Local iconic variables

    // Local control variables
    HTuple  hv_Min, hv_Max, hv_Range, hv_Mult, hv_Add;
    HTuple  hv_Mean, hv_Deviation;

    //
    //归一化 NORM_MIN_MAX
    //
    ConvertImageType(ho_Image, &ho_Image, "real");
    MinMaxGray(ho_Image, ho_Image, 0, &hv_Min, &hv_Max, &hv_Range);
    hv_Mult = (HTuple(1.0).TupleReal())/(hv_Max-hv_Min);
    hv_Add = (-hv_Mult)*hv_Min;
    ScaleImage(ho_Image, &ho_Image, hv_Mult, hv_Add);
    //intensity (Image, Image, Mean, Deviation)
    //min_max_gray (Image, Image, 0, Min, Max, Range)
    //
    //
    //灰度拉伸
    //
    ScaleImage(ho_Image, &ho_Image, hv_Alpha, 1);
    LogImage(ho_Image, &ho_Image, "e");
    Intensity(ho_Image, ho_Image, &hv_Mean, &hv_Deviation);
    MinMaxGray(ho_Image, ho_Image, 0, &hv_Min, &hv_Max, &hv_Range);
    ScaleImage(ho_Image, &(*ho_ResImage), 1.0/hv_Max, 0);
    return;
}

void LEP_HDR (HObject ho_Image, HObject *ho_ResImage, HTuple hv_LogGain, HTuple hv_Alpha,
             HTuple hv_Beta, HTuple hv_WinSize, HTuple hv_Intensity, HTuple hv_GaussSizes,
             HTuple hv_GaussOnOff, HTuple hv_MaxPercentile, HTuple hv_MinPercentile)
{

    // Local iconic variables
    HObject  ho_PreResImage, ho_B2, ho_B1, ho_D2;
    HObject  ho_B0, ho_D1, ho_D0, ho_D_compression2, ho_D_compression1;
    HObject  ho_D_compression0, ho_t, ho_Out, ho_Domain;

    // Local control variables
    HTuple  hv_Mean, hv_Deviation, hv_Rows, hv_Columns;
    HTuple  hv_Grayval, hv_GrayvalSort, hv_Max1, hv_Rmax_clip;
    HTuple  hv_Rmin_clip, hv_Sgn, hv_Indices, hv_Min, hv_Max;
    HTuple  hv_Range, hv_Mult, hv_Add, hv_Type;

    //
    //预处理
    preprocess(ho_Image, &ho_PreResImage, hv_LogGain);
    //
    //lep滤波
    CopyImage(ho_PreResImage, &ho_B2);
    LEP_filter(ho_B2, &ho_B1, hv_Alpha, hv_Beta, HTuple(hv_WinSize[0]));
    SubImage(ho_B2, ho_B1, &ho_D2, 1, 0);
    LEP_filter(ho_B1, &ho_B0, hv_Alpha, hv_Beta, HTuple(hv_WinSize[1]));
    SubImage(ho_B1, ho_B0, &ho_D1, 1, 0);
    Intensity(ho_B0, ho_B0, &hv_Mean, &hv_Deviation);
    ScaleImage(ho_B0, &ho_D0, 1, -hv_Mean);
    //
    //
    //获取高频差-类差分金字塔
    //D2 WinSize[0]滤波后的低频图像
    enhance_details(ho_D2, &ho_D_compression2, hv_Intensity);
    if (0 != (HTuple(hv_GaussOnOff[0])==1))
    {
        GaussFilter(ho_D_compression2, &ho_D_compression2, HTuple(hv_GaussSizes[0]));
    }
    enhance_details(ho_D1, &ho_D_compression1, hv_Intensity);
    if (0 != (HTuple(hv_GaussOnOff[1])==1))
    {
        GaussFilter(ho_D_compression1, &ho_D_compression1, HTuple(hv_GaussSizes[1]));
    }
    enhance_details(ho_D0, &ho_D_compression0, hv_Intensity);
    if (0 != (HTuple(hv_GaussOnOff[2])==1))
    {
        GaussFilter(ho_D_compression0, &ho_D_compression0, HTuple(hv_GaussSizes[2]));
    }
    AddImage(ho_D_compression0, ho_D_compression1, &ho_t, 0.5, 0);
    AddImage(ho_t, ho_D_compression2, &ho_Out, 1, 0);
    //
    //根据百分比阈值，限制图像亮暗
    GetDomain(ho_Out, &ho_Domain);
    GetRegionPoints(ho_Domain, &hv_Rows, &hv_Columns);
    GetGrayval(ho_Out, hv_Rows, hv_Columns, &hv_Grayval);
    TupleSort(hv_Grayval, &hv_GrayvalSort);
    TupleMax(hv_GrayvalSort, &hv_Max1);
    calculatePercentile(hv_GrayvalSort, hv_MaxPercentile, &hv_Rmax_clip);
    calculatePercentile(hv_GrayvalSort, hv_MinPercentile, &hv_Rmin_clip);
    TupleSgn(hv_Grayval.TupleGreaterElem(hv_Rmax_clip), &hv_Sgn);
    TupleFind(hv_Sgn, 1, &hv_Indices);
    hv_Grayval[hv_Indices] = hv_Rmax_clip;
    TupleSgn(hv_Grayval.TupleLessElem(hv_Rmin_clip), &hv_Sgn);
    TupleFind(hv_Sgn, 1, &hv_Indices);
    hv_Grayval[hv_Indices] = hv_Rmin_clip;
    SetGrayval(ho_Out, hv_Rows, hv_Columns, hv_Grayval);
    //
    //归一化
    MinMaxGray(ho_Out, ho_Out, 0, &hv_Min, &hv_Max, &hv_Range);
    hv_Mult = 1.0/(hv_Max-hv_Min);
    hv_Add = (-hv_Mult)*hv_Min;
    ScaleImage(ho_Out, &ho_Out, hv_Mult, hv_Add);
    //
    //输出
    GetImageType(ho_Image, &hv_Type);
    if (0 != (hv_Type==HTuple("uint2")))
    {
        ScaleImage(ho_Out, &ho_Out, 65535, 0);
        ConvertImageType(ho_Out, &(*ho_ResImage), "uint2");
    }
    else
    {
        ScaleImage(ho_Out, &ho_Out, 255, 0);
        ConvertImageType(ho_Out, &(*ho_ResImage), "byte");
    }
    //
    return;
}

void calculatePercentile (HTuple hv_Datas, HTuple hv_percentile, HTuple *hv_Res)
{

    // Local iconic variables

    // Local control variables
    HTuple  hv_n, hv_rank, hv_k, hv_d;

    TupleLength(hv_Datas, &hv_n);
    hv_rank = ((hv_percentile/100.0)*(hv_n-1))+1;
    if (0 != (hv_rank<=1))
    {
        (*hv_Res) = ((const HTuple&)hv_Datas)[0];
        //
    }
    else if (0 != (hv_rank>=hv_n))
    {
        (*hv_Res) = ((const HTuple&)hv_Datas)[hv_n-1];
        //
    }
    else
    {
        hv_k = (hv_rank.TupleFloor()).TupleInt();
        hv_d = hv_rank-hv_k;
        (*hv_Res) = HTuple(hv_Datas[hv_k-1])+(hv_d*(HTuple(hv_Datas[hv_k])-HTuple(hv_Datas[hv_k-1])));
    }
    return;
}

void enhance_details (HObject ho_Image, HObject *ho_ResImage, HTuple hv_m)
{

    // Local iconic variables

    // Local control variables
    HTuple  hv_Min, hv_Max, hv_Range, hv_Mult, hv_Add;

    ScaleImage(ho_Image, &(*ho_ResImage), hv_m.TupleReal(), 0);
    AtanImage((*ho_ResImage), &(*ho_ResImage));
    ScaleImage((*ho_ResImage), &(*ho_ResImage), 2.0/3.141592653589793, 0);
    //
    //
    MinMaxGray((*ho_ResImage), (*ho_ResImage), 0, &hv_Min, &hv_Max, &hv_Range);
    hv_Mult = 1.0/(hv_Max-hv_Min);
    hv_Add = (-hv_Mult)*hv_Min;
    ScaleImage((*ho_ResImage), &(*ho_ResImage), hv_Mult, hv_Add);
    return;
}

void LEP_filter (HObject ho_SrcImage, HObject *ho_ResImage, HTuple hv_A, HTuple hv_B,
                HTuple hv_WinSize)
{

    // Local iconic variables
    HObject  ho_Mean_I, ho_Mean_II, ho_Mean_I2, ho_var_I;
    HObject  ho_x, ho_y, ho_I_gradient, ho_temp, ho_Ak, ho_Bk;

    // Local control variables
    HTuple  hv_Width, hv_Height, hv_N, hv_kernel;

    //
    GetImageSize(ho_SrcImage, &hv_Width, &hv_Height);
    hv_N = (HTuple(1).TupleReal())/(hv_WinSize*hv_WinSize);
    //
    //
    //
    MeanImage(ho_SrcImage, &ho_Mean_I, hv_WinSize, hv_WinSize);
    //scale_image (Mean_I, Mean_I, N, 0)
    //
    MultImage(ho_SrcImage, ho_SrcImage, &ho_Mean_II, 1, 0);
    MeanImage(ho_Mean_II, &ho_Mean_II, hv_WinSize, hv_WinSize);
    //scale_image (Mean_II, Mean_II, N, 0)
    //
    MultImage(ho_Mean_I, ho_Mean_I, &ho_Mean_I2, 1, 0);
    SubImage(ho_Mean_II, ho_Mean_I2, &ho_var_I, 1, 0);
    //
    //
    hv_kernel.Clear();
    hv_kernel[0] = 3;
    hv_kernel[1] = 3;
    hv_kernel[2] = 1;
    hv_kernel[3] = -1;
    hv_kernel[4] = 0;
    hv_kernel[5] = 1;
    hv_kernel[6] = -1;
    hv_kernel[7] = 0;
    hv_kernel[8] = 1;
    hv_kernel[9] = -1;
    hv_kernel[10] = 0;
    hv_kernel[11] = 1;
    ConvolImage(ho_SrcImage, &ho_x, hv_kernel, 0);
    hv_kernel.Clear();
    hv_kernel[0] = 3;
    hv_kernel[1] = 3;
    hv_kernel[2] = 1;
    hv_kernel[3] = -1;
    hv_kernel[4] = -1;
    hv_kernel[5] = -1;
    hv_kernel[6] = 0;
    hv_kernel[7] = 0;
    hv_kernel[8] = 0;
    hv_kernel[9] = 1;
    hv_kernel[10] = 1;
    hv_kernel[11] = 1;
    ConvolImage(ho_SrcImage, &ho_y, hv_kernel, 0);
    AddImage(ho_x, ho_y, &ho_I_gradient, 1, 0);
    AbsImage(ho_I_gradient, &ho_I_gradient);
    PowImage(ho_I_gradient, &ho_I_gradient, 2.0-hv_B);
    //
    //
    MeanImage(ho_I_gradient, &ho_I_gradient, hv_WinSize, hv_WinSize);
    //scale_image (I_gradient, I_gradient, N, 0)
    ScaleImage(ho_I_gradient, &ho_I_gradient, hv_A, 0);
    //
    //
    AddImage(ho_var_I, ho_I_gradient, &ho_temp, 1, 0.00000001);
    DivImage(ho_var_I, ho_temp, &ho_Ak, 1, 0);
    //
    MultImage(ho_Mean_I, ho_Ak, &ho_temp, 1, 0);
    SubImage(ho_Mean_I, ho_temp, &ho_Bk, 1, 0);
    //
    MeanImage(ho_Ak, &ho_Ak, hv_WinSize, hv_WinSize);
    MeanImage(ho_Bk, &ho_Bk, hv_WinSize, hv_WinSize);
    //scale_image (Ak, Ak, N, 0)
    //scale_image (Bk, Bk, N, 0)
    //
    MultImage(ho_Ak, ho_SrcImage, &(*ho_ResImage), 1, 0);
    AddImage((*ho_ResImage), ho_Bk, &(*ho_ResImage), 1, 0);
    //
    return;
}

void sobelEdgeCheck (HObject ho_SrcImage, HObject ho_LEPImage, HObject *ho_ResRegions,
                    HTuple hv_MinSize)
{

    // Local iconic variables
    HObject  ho_GrayImage, ho_EdgeAmplitude, ho_Region;
    HObject  ho_ConnectedRegions, ho_SelectedRegions, ho_ImageReduced;
    HObject  ho_Region1, ho_Region2;

    // Local control variables
    HTuple  hv_Min, hv_Max, hv_Range, hv_UsedThreshold;

    Rgb1ToGray(ho_SrcImage, &ho_GrayImage);

    SobelAmp(ho_GrayImage, &ho_EdgeAmplitude, "sum_sqrt", 3);
    MinMaxGray(ho_EdgeAmplitude, ho_EdgeAmplitude, 0, &hv_Min, &hv_Max, &hv_Range);
    Threshold(ho_EdgeAmplitude, &ho_Region, 5, hv_Max);
    Connection(ho_Region, &ho_ConnectedRegions);
    SelectShape(ho_ConnectedRegions, &ho_SelectedRegions, "area", "and", 1, 200);

    ReduceDomain(ho_GrayImage, ho_SelectedRegions, &ho_ImageReduced);
    BinaryThreshold(ho_ImageReduced, &ho_Region1, "max_separability", "dark", &hv_UsedThreshold);
    Threshold(ho_GrayImage, &ho_Region2, hv_UsedThreshold, 255);
    return;
}

void laplaceEdgeCheck (HObject ho_SrcImage, HObject ho_LEPImage, HObject *ho_ResRegions,
                      HTuple hv_MinSize)
{


}

void checkSolder (HObject ho_SrcImage, HObject ho_LEPImage, HObject *ho_Region, HTuple hv_MinRB,
                 HTuple hv_MaskSize, HTuple *hv_Result)
{

    // Local iconic variables
    HObject  ho_DarkRegion, ho_ImaAmp, ho_ImaDir;
    HObject  ho_Regions;

    // Local control variables
    HTuple  hv_OKFlag, hv_NGFlag, hv_Number, hv_DisplaySize;
    HTuple  hv_Exception;

    //检测焊料溢出 -- 边缘分割
    try
    {
        hv_OKFlag = 0;
        hv_NGFlag = 1;
        (*hv_Result) = hv_OKFlag;


        //binary_threshold (SrcImage, DarkRegion, 'max_separability', 'dark', UsedThreshold)
        //connection (DarkRegion, DarkRegion)
        //closing_circle (DarkRegion, DarkRegion, 3.5)
        //fill_up (DarkRegion, DarkRegion)
        Threshold(ho_SrcImage, &ho_DarkRegion, 0, 31);
        ClosingCircle(ho_DarkRegion, &ho_DarkRegion, 3.5);
        //dilation_rectangle1 (DarkRegion, DarkRegion, MaskSize, MaskSize)
        DilationCircle(ho_DarkRegion, &ho_DarkRegion, hv_MaskSize);



        //binary_threshold (SrcImage, Region, 'max_separability', 'dark', UsedThreshold)
        EdgesImage(ho_SrcImage, &ho_ImaAmp, &ho_ImaDir, "shen", 1, "nms", 10, 20);
        Threshold(ho_ImaAmp, &(*ho_Region), 5, 255);
        ClosingCircle((*ho_Region), &(*ho_Region), 3.5);
        Connection((*ho_Region), &(*ho_Region));
        SelectShapeStd((*ho_Region), &(*ho_Region), "max_area", 70);
        ClosingCircle((*ho_Region), &(*ho_Region), 51.5);
        OpeningCircle((*ho_Region), &(*ho_Region), 2);
        Difference((*ho_Region), ho_DarkRegion, &(*ho_Region));
        Connection((*ho_Region), &(*ho_Region));


        SelectShape((*ho_Region), &(*ho_Region), (HTuple("area").Append("inner_radius")),
                    "and", HTuple(500).TupleConcat(hv_MinRB), (HTuple(5000).Append(100)));


        AutoThreshold(ho_SrcImage, &ho_Regions, 0.6);
        SelectShape(ho_Regions, &ho_Regions, "area", "and", 5, 20000);
        Union1(ho_Regions, &ho_Regions);
        ClosingCircle(ho_Regions, &ho_Regions, 7);
        Difference(ho_Regions, ho_DarkRegion, &ho_Regions);
        OpeningCircle(ho_Regions, &ho_Regions, 3);

        Connection(ho_Regions, &ho_Regions);
        SelectShape(ho_Regions, &ho_Regions, (HTuple("area").Append("inner_radius")),
                    "and", HTuple(500).TupleConcat(hv_MinRB), (HTuple(999999).Append(100)));


        Union2((*ho_Region), ho_Regions, &(*ho_Region));
        Connection((*ho_Region), &(*ho_Region));
        SelectShape((*ho_Region), &(*ho_Region), "area", "and", 5, 99999);
        CountObj((*ho_Region), &hv_Number);
        if (0 != (hv_Number>0))
        {
            (*hv_Result) = hv_NGFlag;
        }

        hv_DisplaySize = 9;
        ShapeTrans((*ho_Region), &(*ho_Region), "rectangle1");
        DilationRectangle1((*ho_Region), &(*ho_Region), hv_DisplaySize, hv_DisplaySize);
        Union1((*ho_Region), &(*ho_Region));

    }
    // catch (Exception)
    catch (HalconCpp::HException &HDevExpDefaultException)
    {
        HDevExpDefaultException.ToHTuple(&hv_Exception);
        (*hv_Result) = hv_NGFlag;
    }

    return;
}

void checkDotShadow (HObject ho_SrcImage, HObject ho_LEPImage, HObject ho_LEPImageShadow,
                    HObject *ho_Region, HTuple hv_MinArea, HTuple hv_MinNumber, HTuple hv_MinRatio,
                    HTuple hv_MaskSize, HTuple hv_MinDiameter, HTuple *hv_Result)
{

    // Local iconic variables
    HObject  ho_DarkRegion, ho_Domain, ho_RegionErosion;
    HObject  ho_ImageConverted, ho_ImageLaplace, ho_RegionSmall;
    HObject  ho_RegionShadow;

    // Local control variables
    HTuple  hv_OKFlag, hv_NGFlag, hv_Mean, hv_Deviation;
    HTuple  hv_MinSrcImage, hv_Max, hv_Range, hv_UsedThreshold;
    HTuple  hv_idx, hv_Number, hv_Width, hv_Height, hv_Area;
    HTuple  hv_Row, hv_Column, hv_ratio, hv_DisplaySize, hv_Exception;

    hv_OKFlag = 0;
    hv_NGFlag = 1;
    (*hv_Result) = hv_OKFlag;
    try
    {

        Intensity(ho_SrcImage, ho_SrcImage, &hv_Mean, &hv_Deviation);
        MinMaxGray(ho_SrcImage, ho_SrcImage, 0, &hv_MinSrcImage, &hv_Max, &hv_Range);
        if (0 != (hv_Mean<120))
        {
            BinaryThreshold(ho_SrcImage, &ho_DarkRegion, "max_separability", "dark", &hv_UsedThreshold);
            Connection(ho_DarkRegion, &ho_DarkRegion);
            ClosingCircle(ho_DarkRegion, &ho_DarkRegion, 3.5);
            FillUp(ho_DarkRegion, &ho_DarkRegion);
            DilationCircle(ho_DarkRegion, &ho_DarkRegion, hv_MaskSize);
        }
        else
        {
            Threshold(ho_SrcImage, &ho_DarkRegion, 0, 50);
            DilationCircle(ho_DarkRegion, &ho_DarkRegion, hv_MaskSize);
        }
        GetDomain(ho_SrcImage, &ho_Domain);
        ErosionRectangle1(ho_Domain, &ho_RegionErosion, 11, 11);
        Difference(ho_Domain, ho_RegionErosion, &ho_Domain);
        Union2(ho_Domain, ho_DarkRegion, &ho_DarkRegion);

        //median_rect (SrcImage, SrcImage, 3, 3)


        ConvertImageType(ho_SrcImage, &ho_ImageConverted, "real");

        //小黑点
        LaplaceOfGauss(ho_ImageConverted, &ho_ImageLaplace, 4);
        for (hv_idx=1; hv_idx<=12; hv_idx+=1)
        {
            GaussFilter(ho_ImageLaplace, &ho_ImageLaplace, 5);
        }

        Threshold(ho_ImageLaplace, &ho_RegionSmall, 4, 255);
        Connection(ho_RegionSmall, &ho_RegionSmall);
        //判定方式之一: 满足最小面积 或 最小直径
        SelectShape(ho_RegionSmall, &ho_RegionSmall, (HTuple("area").Append("max_diameter")),
                    "or", hv_MinArea.TupleConcat(hv_MinDiameter), (HTuple(500).Append(100)));


        //laplace_of_gauss (ImageConverted, ImageLaplace, 3)
        //min_max_gray (ImageLaplace, ImageLaplace, 0, Min, Max, Range)
        //for idx := 1 to 12 by 1
        //gauss_filter (ImageLaplace, ImageLaplace, 5)
        //endfor


        //min_max_gray (ImageLaplace, ImageLaplace, 0, Min1, Max1, Range1)
        //MinThresh := max2(Max1*0.1, 5)
        //MaxThresh := max2(MinThresh+5, Max)
        //threshold (ImageLaplace, Region, MinThresh, MaxThresh)
        //closing_rectangle1 (Region, Region, 10, 10)
        //connection (Region, Region)
        //select_shape (Region, Region, ['area','max_diameter'], ['and'], [ MinArea, MinDiameter], [ 500, 100])
        //connection (Region, Region)
        //select_shape (Region, Region, 'area', 'and', 5, 99999)

        //阴影
        LaplaceOfGauss(ho_ImageConverted, &ho_ImageLaplace, 9);
        for (hv_idx=1; hv_idx<=12; hv_idx+=1)
        {
            GaussFilter(ho_ImageLaplace, &ho_ImageLaplace, 5);
        }
        Threshold(ho_ImageLaplace, &ho_RegionShadow, 2, 255);
        ClosingRectangle1(ho_RegionShadow, &ho_RegionShadow, 10, 10);
        Connection(ho_RegionShadow, &ho_RegionShadow);
        SelectShape(ho_RegionShadow, &ho_RegionShadow, (HTuple("area").Append("max_diameter")),
                    "or", HTuple(250).TupleConcat(hv_MinDiameter*2), (HTuple(700).Append(100)));
        Connection(ho_RegionShadow, &ho_RegionShadow);
        SelectShape(ho_RegionShadow, &ho_RegionShadow, "area", "and", 5, 99999);
        //laplace_of_gauss (LEPImage, ImageLaplace, 5)
        //zero_crossing (ImageLaplace, RegionCrossing)
        //threshold (ImageLaplace, RegionShadow, 10, 255)
        //closing_circle (RegionShadow, RegionShadow, 4)

        //opening_circle (RegionShadow, RegionShadow, 3.5)

        //connection (RegionShadow, RegionShadow)
        //select_shape (RegionShadow, RegionShadow, ['area'], ['and'], [ MinArea*20], [ 1000])

        //union2 (Region, RegionSmall, Region)
        Union2(ho_RegionSmall, ho_RegionShadow, &(*ho_Region));

        Difference((*ho_Region), ho_DarkRegion, &(*ho_Region));
        Connection((*ho_Region), &(*ho_Region));
        SelectShape((*ho_Region), &(*ho_Region), "area", "and", hv_MinArea, 99999);
        //判定方式之二 ： 黑点个数或阴影个数大于MinNumber
        CountObj((*ho_Region), &hv_Number);
        if (0 != (hv_Number>hv_MinNumber))
        {
            (*hv_Result) = hv_NGFlag;
        }

        //判定方式之三 ： 轮廓尺寸与图片面积比例大于MinRatio
        //所有异物尺寸
        //union1 (Region, RegionUnion)
        //get_image_size (EdgeAmplitude, Width, Height)
        //area_center (RegionUnion, Area, Row, Column)
        //ratio := real(Area) / (Height * Width)
        //if (ratio > MinRatio)
        //Result := NGFlag
        //endif
        //单个异物尺寸
        GetImageSize(ho_SrcImage, &hv_Width, &hv_Height);
        AreaCenter((*ho_Region), &hv_Area, &hv_Row, &hv_Column);
        if (0 != (hv_Area>0.0))
        {
            TupleMax(hv_Area, &hv_Area);
            hv_ratio = (hv_Area.TupleReal())/(hv_Height*hv_Width);
            if (0 != (hv_ratio>hv_MinRatio))
            {
                (*hv_Result) = hv_NGFlag;
            }
        }




        hv_DisplaySize = 9;
        ShapeTrans((*ho_Region), &(*ho_Region), "rectangle1");
        DilationRectangle1((*ho_Region), &(*ho_Region), hv_DisplaySize, hv_DisplaySize);
        Union1((*ho_Region), &(*ho_Region));
    }
    // catch (Exception)
    catch (HalconCpp::HException &HDevExpDefaultException)
    {
        HDevExpDefaultException.ToHTuple(&hv_Exception);
        (*hv_Result) = hv_NGFlag;
    }


    return;
}

void Check (HObject ho_SrcImage, HObject *ho_ResImage, HTuple hv_MinArea, HTuple hv_MinNumber,
           HTuple hv_MinRatio, HTuple hv_MaskSize, HTuple hv_MinDiameter, HTuple hv_PixelSize,
           HTuple *hv_Result)
{

    // Local iconic variables
    HObject  ExpTmpLocalVar_LEPImage, ho_GreenRegions;
    HObject  ho_RedRegions, ho_SolderRegions, ho_DotShadowRegions;

    // Local control variables
    HTuple  hv_OKFlag, hv_NGFlag, hv_ShowOKNG, hv_Width;
    HTuple  hv_Height, hv_LogGain, hv_Alpha, hv_Beta, hv_WinSize;
    HTuple  hv_Intensity, hv_GaussSizes, hv_GaussOnOff, hv_MaxPercentile;
    HTuple  hv_MinPercentile, hv_Start, hv_MinRBOfPad, hv_SolderResult;
    HTuple  hv_DotShadowResult, hv_WindowHandle, hv_Message;
    HTuple  hv_OKNGFontSize, hv_Ascent, hv_Descent, hv_WidthStr;
    HTuple  hv_HeightStr, hv_Exception;

    //global object LEPImage
    try
    {
        (*hv_Result) = 0;
        hv_OKFlag = 0;
        hv_NGFlag = 1;
        hv_ShowOKNG = 1;



        GenEmptyRegion(&ho_GreenRegions);
        GenEmptyRegion(&ho_RedRegions);
        CopyImage(ho_SrcImage, &(*ho_ResImage));
        GetImageSize(ho_SrcImage, &hv_Width, &hv_Height);

        //fft_generic (SrcImage, ImageFFT, 'to_freq', -1, 'sqrt', 'dc_center', 'complex')
        //gen_lowpass (ImageLowpass, 0.3, 'none', 'dc_center', Width, Height)
        //convol_fft (ImageFFT, ImageLowpass, ImageConvol)
        //fft_generic (ImageConvol, SrcImage, 'from_freq', 1, 'sqrt', 'dc_center', 'byte')

        hv_LogGain = 1;
        hv_Alpha = 16;
        hv_Beta = 0.1;
        hv_WinSize.Clear();
        hv_WinSize[0] = 31;
        hv_WinSize[1] = 51;
        hv_Intensity = 1;
        hv_GaussSizes.Clear();
        hv_GaussSizes[0] = 11;
        hv_GaussSizes[1] = 11;
        hv_GaussSizes[2] = 11;
        hv_GaussOnOff.Clear();
        hv_GaussOnOff[0] = 1;
        hv_GaussOnOff[1] = 1;
        hv_GaussOnOff[2] = 1;
        hv_MaxPercentile = 99.5;
        hv_MinPercentile = 0.05;
        CountSeconds(&hv_Start);
        LEP_HDR(ho_SrcImage, &ExpTmpLocalVar_LEPImage, hv_LogGain, hv_Alpha, hv_Beta,
                hv_WinSize, hv_Intensity, hv_GaussSizes, hv_GaussOnOff, hv_MaxPercentile,
                hv_MinPercentile);
        ExpSetGlobalVar_LEPImage(ExpTmpLocalVar_LEPImage);


        //检测 焊料溢出
        hv_MinRBOfPad = 7;
        hv_SolderResult = 0;
        checkSolder(ho_SrcImage, ExpGetGlobalVar_LEPImage(), &ho_SolderRegions, hv_MinRBOfPad,
                    hv_MaskSize, &hv_SolderResult);
        if (0 != (hv_SolderResult==hv_NGFlag))
        {
            ConcatObj(ho_RedRegions, ho_SolderRegions, &ho_RedRegions);
        }


        //检测 黑点、阴影
        hv_DotShadowResult = hv_OKFlag;
        hv_MinDiameter = hv_MinDiameter/hv_PixelSize;
        if (0 != (hv_SolderResult==hv_OKFlag))
        {
            checkDotShadow(ho_SrcImage, ExpGetGlobalVar_LEPImage(), ExpGetGlobalVar_LEPImage(),
                           &ho_DotShadowRegions, hv_MinArea, hv_MinNumber, hv_MinRatio, hv_MaskSize,
                           hv_MinDiameter, &hv_DotShadowResult);
            if (0 != (hv_DotShadowResult==hv_NGFlag))
            {
                ConcatObj(ho_RedRegions, ho_DotShadowRegions, &ho_RedRegions);
            }
        }
        Union1(ho_RedRegions, &ho_RedRegions);
        ShapeTrans(ho_RedRegions, &ho_RedRegions, "rectangle1");
        Connection(ho_RedRegions, &ho_RedRegions);


        (*hv_Result) = hv_SolderResult.TupleOr(hv_DotShadowResult);

        OpenWindow(0, 0, hv_Width, hv_Height, 0, "buffer", "", &hv_WindowHandle);
        SetPart(hv_WindowHandle, 0, 0, hv_Height-1, hv_Width-1);
        DispImage(ho_SrcImage, hv_WindowHandle);
        if (0 != ((*hv_Result)==hv_OKFlag))
        {
            hv_Message = "OK";
            SetColor(hv_WindowHandle, "green");
        }
        else
        {
            hv_Message = "NG";
            SetColor(hv_WindowHandle, "red");
        }

        hv_OKNGFontSize = 81;
        SetFont(hv_WindowHandle, ("-Consolas-"+hv_OKNGFontSize)+"-");
        GetStringExtents(hv_WindowHandle, hv_Message, &hv_Ascent, &hv_Descent, &hv_WidthStr,
                         &hv_HeightStr);
        SetTposition(hv_WindowHandle, 12, (hv_Width-hv_WidthStr)/2);

        if (0 != (hv_ShowOKNG==1))
        {
            WriteString(hv_WindowHandle, hv_Message);
        }

        if (0 != (HTuple(hv_SolderResult==hv_NGFlag).TupleOr(hv_DotShadowResult==hv_NGFlag)))
        {
            SetLineWidth(hv_WindowHandle, 3);
            SetDraw(hv_WindowHandle, "margin");
            DispRegion(ho_RedRegions, hv_WindowHandle);
        }

        DumpWindowImage(&(*ho_ResImage), hv_WindowHandle);
    }
    // catch (Exception)
    catch (HalconCpp::HException &HDevExpDefaultException)
    {
        HDevExpDefaultException.ToHTuple(&hv_Exception);
        (*hv_Result) = 1;

    }
}
